package eu.floranet.arquivo.model.persistence;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.InvalidPropertiesFormatException;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;

import eu.floranet.arquivo.model.persistence.exception.EmptyLinkFile;
import eu.floranet.arquivo.model.persistence.exception.EmptyLinkProperties;
import eu.floranet.arquivo.model.persistence.exception.LinkException;
import eu.floranet.arquivo.model.persistence.link.PersistenceLinkEntityHandler;
import eu.floranet.arquivo.utils.Base64;

/**
 * Classe que permita gerir os objectos {@link PersistenceLinkFile}, {@link PersistenceLinkProperties}.
 * Esta classe é extendida da classe {@link PersistenceLinkHandler} e tem como definição
 * o manuseamento dos objectos. Dado o manuseamento desses objectos esta classe cria dos valores
 * passados ou contidos num ficheiro uma lista tipo {@literal Map<String, String>}.</br>
 * Se o ficheiro de configuração já existir esta classe pode ler as propriedades contidas no ficheiro
 * e criar desde logo o mapa de configuração.
 * <p>
 * Exemplo:
 * </p><p>
 * 		Properties prop = new Properties();</br>
 *		prop.put("server", "A");</br>
 *		prop.put("porta", "B");</br>
 *	 	prop.put("database", "C");</br>
 *		prop.put("user", "D");</br>
 *		prop.put("pass", "E");</br>
 *		</br>
 *		PersistenceLinkManager plm = new PersistenceLinkManager();</br>
 *		plm.getPersistenceLinkFile().setLinkFile(new File("database.xml"));</br>
 *		plm.getPersistenceLinkProperties().setLinkProperties(prop);</br>
 *		plm.savePersistenceProperties();</br>
 *		plm.addPersistenceLinkEntity(new PersistenceLinkEntity(plm));</br>
 *		</br>
 *		PersistenceLinkManager plm = new PersistenceLinkManager();</br>
 *		plm.getPersistenceLinkFile().setLinkFile(new File("database.xml"));</br>
 *		plm.readPersistenceProperties();</br>
 *		plm.getPropertiesMap();</br>
 *		</br>
 *		PersistenceLinkManager plm = new PersistenceLinkManager();</br>
 *		plm.getPersistenceLinkFile().setLinkFile(new File("database.xml"));</br>
 *		plm.readPersistenceProperties();</br>
 *		plm.addPersistenceLinkEntity(new PersistenceLinkEntity(plm));</br>
 * </p>
 * @author jmark
 *
 */
public class PersistenceLinkManager extends PersistenceLinkHandler implements IPersistenceLinkManager {

	/**
	 *  Mapa de atribuição de dados pelos objectos criados.
	 */
	private Map<String, String> mapProp = null;
	private List<PersistenceLinkEntityHandler> listaPersistenceLinkEntity = new ArrayList<PersistenceLinkEntityHandler>();
	
	/**
	 * Método de default de construção da classe. Este método atribui ás suas propriedades
	 * valores nulos.
	 */
	public PersistenceLinkManager() {
		super();
	}

	/**
	 * Método de construção da classe. Este método permite criar o objecto já
	 * com o ficheiro de definição aonde os valores das propriedades vão ser
	 * guardados.
	 * 
	 * @param persistenceLinkFile - PersistenceLinkFile
	 */
	public PersistenceLinkManager(PersistenceLinkFile persistenceLinkFile) {
		super(persistenceLinkFile);
	}

	/**
	 * Método de construção da classe. Este método cria o objecto com os valores
	 * de configuração, sem definição de onde possam ser guardados. Este é um método tipo
	 * volatil, após destruição da classe esta perde os valores que lhe foram atribuidos.
	 * 
	 * @param persistenceLinkProperties - PersistenceLinkProperties
	 */
	public PersistenceLinkManager(
			PersistenceLinkProperties persistenceLinkProperties) {
		super(persistenceLinkProperties);
	}
	
	/**
	 * Método de construção da classe. Este método permite criar o objecto
	 * já com o ficheiro de definição aonde os valores vão ser guardados e os valores
	 * a ser guardados.
	 * 
	 * @param persistenceLinkFile - PersistenceLinkFile
	 * @param persistenceLinkProperties - PersistenceLinkProperties
	 */
	public PersistenceLinkManager(PersistenceLinkFile persistenceLinkFile,
			PersistenceLinkProperties persistenceLinkProperties) {
		super(persistenceLinkFile, persistenceLinkProperties);
		
	}
	
	/* (non-Javadoc)
	 * @see eu.floranet.arquivo.model.persistence.IPersistenceLinkManager#getPropertiesMap()
	 */
	public Map<String, String> getPropertiesMap() {
		return this.mapProp;
	}

	/* (non-Javadoc)
	 * @see eu.floranet.arquivo.model.persistence.IPersistenceLinkManager#setPropertiesMap(java.util.Map)
	 */
	public void setPropertiesMap(Map<String, String> mapProp) {
		this.mapProp = mapProp;
	}
	
	/* (non-Javadoc)
	 * @see eu.floranet.arquivo.model.persistence.IPersistenceLinkManager#savePersistenceProperties()
	 */
	public void savePersistenceProperties() throws EmptyLinkProperties, EmptyLinkFile, LinkException{
		if(getPersistenceLinkProperties().isLinkPropertiesEmpty()){
			throw new EmptyLinkProperties(
					"A lista de propriedades encontra-se vazia ou nula.",
					new Throwable(
							"\nClasse: eu.floranet.arquivo.model.persistence.PersistenceLinkManager" +
							"\nMétodo: savePersistenceProperties()" +
							"\nLinha: 25"));
		} else if(getPersistenceLinkFile().isLinkFileEmpty()){
			throw new EmptyLinkFile(
					"O ficheiro de configuração não foi atribuido.",
					new Throwable(
							"\nClasse: eu.floranet.arquivo.model.persistence.PersistenceLinkManager" +
							"\nMétodo: savePersistenceProperties()" +
							"\nLinha: 31"));
		} else {
			try {
				FileOutputStream fos = new FileOutputStream(getPersistenceLinkFile().getLinkFile());
				getPersistenceLinkProperties().getLinkProperties().storeToXML(fos, "DatabaseModel");
				fos.close();
				readPersistenceProperties();
			} catch (FileNotFoundException e) {
				throw new LinkException(e.getMessage(), e.getCause());
			} catch (IOException e) {
				throw new LinkException(e.getMessage(), e.getCause());
			}
		}
		
	}
	
	/* (non-Javadoc)
	 * @see eu.floranet.arquivo.model.persistence.IPersistenceLinkManager#readPersistenceProperties()
	 */
	public void readPersistenceProperties() throws EmptyLinkProperties, EmptyLinkFile, LinkException{
		if(getPersistenceLinkFile().existLinkFile()){
			try {
				FileInputStream fis = new FileInputStream(getPersistenceLinkFile().getLinkFile());
				getPersistenceLinkProperties().getLinkProperties().loadFromXML(fis);
				
				if(this.mapProp == null){
					Properties properties = getPersistenceLinkProperties().getLinkProperties();
					
					if(!getPersistenceLinkProperties().isLinkPropertiesEmpty()){
						this.mapProp = new HashMap<String, String>();
						
						for (Iterator<Object> it = properties.keySet().iterator(); it.hasNext();) {
							Object key = it.next();
							Object value = properties.get(key);
							
							if(key.equals("eclipselink.jdbc.password")){
								String v = value.toString();
								this.mapProp.put((String) key,(String) Base64.decodeToObject(v));
							} else {
								this.mapProp.put((String) key, (String) value);
							}
						}
						
					} else {
						throw new EmptyLinkProperties(
								"A lista de propriedades não foram encontradas no ficheiro. Propriedades no ficheiro nulas.",
								new Throwable(
										"\nClasse: eu.floranet.arquivo.model.persistence.PersistenceLinkManager" +
										"\nMétodo: readPersistenceProperties()" +
										"\nLinha: 85"));
					}
				}
				
				fis.close();
			} catch (FileNotFoundException e) {
				throw new LinkException(e.getMessage(), e.getCause());
			} catch (InvalidPropertiesFormatException e) {
				throw new LinkException(e.getMessage(), e.getCause());
			} catch (IOException e) {
				throw new LinkException(e.getMessage(), e.getCause());
			}
			
		} else if(!getPersistenceLinkProperties().isLinkPropertiesEmpty()){
			if(this.mapProp == null){
				Properties properties = getPersistenceLinkProperties().getLinkProperties();
				this.mapProp = new HashMap<String, String>();
				
				for (Iterator<Object> it = properties.keySet().iterator(); it.hasNext();) {
					Object key = it.next();
					Object value = properties.get(key);
					
					if(key.equals("eclipselink.jdbc.password")){
						String v = value.toString();
						this.mapProp.put((String) key,(String) Base64.decodeToObject(v));
					} else {
						this.mapProp.put((String) key, (String) value);
					}
					
				}
				
			}
		} else {
			throw new LinkException(
					"Propriedades dos objectos PersistenceLinkProperties e PersistenceLinkFile nulas",
					new Throwable(
							"\nClasse: eu.floranet.arquivo.model.persistence.PersistenceLinkManager" +
							"\nMétodo: readPersistenceProperties()" +
							"\nLinha: 127"));
		}
		
	}

	public List<PersistenceLinkEntityHandler> getListaPersistenceLinkEntity() {
		return this.listaPersistenceLinkEntity;
	}

	public void setListaPersistenceLinkEntity(
			List<PersistenceLinkEntityHandler> listaPersistenceLinkEntity) {
		this.listaPersistenceLinkEntity = listaPersistenceLinkEntity;
	}

	public PersistenceLinkEntityHandler addPersistenceLinkEntity(PersistenceLinkEntityHandler persistenceLinkEntityHandler){
		getListaPersistenceLinkEntity().add(persistenceLinkEntityHandler);
		return persistenceLinkEntityHandler;
	}
	
	public PersistenceLinkEntityHandler removePersistenceLinkEntity(PersistenceLinkEntityHandler persistenceLinkEntityHandler){
		getListaPersistenceLinkEntity().remove(persistenceLinkEntityHandler);
		persistenceLinkEntityHandler.setPersistenceLinkManager(null);
		return persistenceLinkEntityHandler;
	}
	
	/**
	 * Método de verificação se a lista tipo {@literal Mapa<String, String>} existe e não se encontra vazia.
	 * Se alguma destas condições se verificar retorna verdadeiro.
	 * 
	 * @return boolean
	 */
	public boolean isPropertiesMapEmpty(){
		if(this.mapProp != null){
			if(this.mapProp.isEmpty()){
				return true;
			} else {
				return false;
			}
		} else {
			return true;
		}
	}
}
